---
author: Marcus Rohrmoser
categories:
- en
- development
date: "2009-12-09T03:32:34+00:00"
tags:
- Cocoa
- CoreData
- iPhone
- NSEntityDescription
- NSManagedObject
- NSManagedObjectContext
- NSPredicate
- Objective C
title: CoreData generic findManyByKey
type: post
url: /2009/12/coredata-generic-findmanybykey/
yourls_shorturl:
- http://s.mro.name/2x
---
The base for many of my [SELECT][1]-ish queries when querying by exact match is one generic method I created in some [category methods][2] on [NSManagedObjectContext][3]:

<pre class="line-numbers"><code class="language-objc">-(NSArray*)entityName:(NSString*)entityName findManyByRelation:(NSDictionary*)dict
{
//  TODO handle dict nil and emptyness
    NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:dict.count];
    for(NSString *key in dict)
    {
        NSExpression *left = [NSExpression expressionForKeyPath:key];
        NSExpression *right = [NSExpression expressionForConstantValue:[dict objectForKey:key]];
        NSComparisonPredicate *cp = [[NSComparisonPredicate alloc] initWithLeftExpression:left
            rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
        [arr addObject:cp];
        [cp release];
    }
    NSPredicate *pred = nil;
    if(arr.count == 1)
        pred = [[arr objectAtIndex:0] retain];    // why do I have to retain here?
    else
        pred = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:arr];

    NSFetchRequest *fr = [[NSFetchRequest alloc] init];
    fr.entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
    fr.predicate = pred;
    [arr release];
    [pred release];
//  NSLog(@"predicate effective: %@", fr.predicate);

    NSError *err = nil;
    NSArray *ps = [self executeFetchRequest:fr error:&err];
    if(err != nil)
        [NSException raise:@"DB Error" format:@"Fetch problem %@", fr.predicate.predicateFormat];
    [fr release];
    return ps;
}
</code></pre>

For example it serves under the hood of it's sibling helpers

<pre class="line-numbers"><code class="language-objc">-(NSManagedObject*)entityName:(NSString*)entityName findByPrimaryKey:(NSDictionary*)dict
{
    NSArray *ps = [self entityName:entityName findManyByRelation:dict];
    if(ps == nil || ps.count == 0)
        return nil;
    if (ps.count == 1)
        return [ps objectAtIndex:0];
    [NSException raise:@"DB Error" format:@"Multiple hits for %@", dict];
    return (NSManagedObject*)1/0;
}
</code></pre>

and

<pre class="line-numbers"><code class="language-objc">-(NSManagedObject*)entityName:(NSString*)entityName selectOrInsertWithKey:(NSDictionary*)dict
{
    NSManagedObject *o = [self entityName:entityName findByPrimaryKey:dict];
    if(o == nil)
    {
        NSEntityDescription *ed = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
        o = [[NSManagedObject alloc] initWithEntity:ed insertIntoManagedObjectContext:self];
        for(NSString *key in dict)
            [o setValue:[dict objectForKey:key] forKey:key];
        [o autorelease];
    }
    return o;
}
</code></pre>

Those methods again are called by custom convenience wrappers in the [model classes][4] loosely following the [ActiveRecord Pattern][5].

 [1]: http://en.wikipedia.org/wiki/Select_%28SQL%29
 [2]: http://developer.apple.com/iphone/library/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW1
 [3]: http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html
 [4]: http://developer.apple.com/iPhone/library/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html
 [5]: http://martinfowler.com/eaaCatalog/activeRecord.html
